﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX;

namespace EdytorWF
{
    class Camera
    {
        public Matrix ViewMatrix { get; set; }
        public Quaternion RotationQuaternion { get; set; }
        public Vector3 Position { get; set; }
        public Vector3 Forward { get; set; }
        public Vector3 Up { get; set; }
        public Vector3 Right { get; set; }

        private float yaw = 0.1f; //rotation around the y axis
        private float pitch = 0.1f; //rotation around the x axis
        private float roll = 0.1f; //rotation around the z axis

        public float Fov { get; set; }
        public float AspectRatio { get; set;}
        public float ZNear { get; set; }
        public float ZFar { get; set; }
        public float hFrac { get; set; }
        public float vFrac { get; set; }

        public Camera()
        {
            Position = new Vector3(10, 10, 0);
            Forward = new Vector3(0.0f, 0.0f, 1.0f);
            Up = new Vector3(0.0f, 1.0f, 0.0f);
            Right = new Vector3(1.0f, 0.0f, 0.0f);

            Fov = (float)Math.PI / 4;
            AspectRatio = 1;
            hFrac = (float)Math.Tan(Fov * 0.5);
            vFrac = (float)Math.Tan(Fov * 0.5 * AspectRatio);
            ZNear = 1.0f;
            ZFar = 100000.0f;
        }

        public override String ToString()
        {
            return String.Format("Camera Position X:{0} Y:{1} Z:{2} \nForward X:{3} Y:{4} Z:{5} \nRight X:{6} Y:{7} Z:{8} \nUP X:{9} Y:{10} Z:{11}", 
                Position.X, Position.Y, Position.Z, Forward.X, Forward.Y, Forward.Z, Right.X, Right.Y, Right.Z, Up.X, Up.Y, Up.Z);
        }

        public void Yaw(float amount)
        {
            yaw += amount;
            yaw = yaw % 360;
        }

        public void Pitch(float amount)
        {
            pitch += amount;
            pitch = pitch % 360;
        }

        public void Roll(float amount)
        {
            roll += amount;
            roll = roll % 360;
        }

        private void Rotate()
        {
            Quaternion YawRotation, pitchRotation,
                YawResult, pitchResult;

            YawRotation = new Quaternion();
            YawRotation.X = 0;
            YawRotation.Y = (float)Math.Sin(ToRadians(yaw) / 2);
            YawRotation.Z = 0;
            YawRotation.W = (float)Math.Cos(ToRadians(yaw) / 2);

            pitchRotation = new Quaternion();
            pitchRotation.X = (float)Math.Sin(ToRadians(pitch) / 2);
            pitchRotation.Y = 0;
            pitchRotation.Z = 0;
            pitchRotation.W = (float)Math.Cos(ToRadians(pitch) / 2);

            YawRotation.Multiply(pitchRotation);

            YawResult = (YawRotation * new Quaternion(0, 0, 1, 0)) * Quaternion.Conjugate(YawRotation);

            Forward = new Vector3(YawResult.X, YawResult.Y, YawResult.Z);

            pitchResult = (YawRotation * new Quaternion(1, 0, 0, 0)) * Quaternion.Conjugate(YawRotation);

            Right = new Vector3(pitchResult.X, pitchResult.Y, pitchResult.Z);

            Up = Vector3.Cross(Forward, Right);
        }

        /*private void FillViewMatrix()
        {
            Matrix matrix = Matrix.Identity;

            matrix.M11 = Right.X; matrix.M12 = Up.X; matrix.M13 = Forward.X;
            matrix.M21 = Right.Y; matrix.M22 = Up.Y; matrix.M23 = Forward.Y;
            matrix.M31 = Right.Z; matrix.M32 = Up.Z; matrix.M33 = Forward.Z;

            matrix.M41 = -Vector3.Dot(Position, Right);
            matrix.M42 = -Vector3.Dot(Position, Up);
            matrix.M43 = -Vector3.Dot(Position, Forward);

            ViewMatrix = matrix;
        }*/

        public void Rotate(float p, float y, float r)
        {
            Pitch(p);
            Yaw(y);
            Rotate();
        }

        public Vector3 GetForwardRotate(float p, float y, float r)
        {
            float new_p = pitch + p;
            new_p = new_p % 360;

            float new_y = yaw + y;
            new_y = new_y % 360;

            Quaternion YawRotation, pitchRotation,
                YawResult;

            YawRotation = new Quaternion();
            YawRotation.X = 0;
            YawRotation.Y = (float)Math.Sin(ToRadians(yaw) / 2);
            YawRotation.Z = 0;
            YawRotation.W = (float)Math.Cos(ToRadians(yaw) / 2);

            pitchRotation = new Quaternion();
            pitchRotation.X = (float)Math.Sin(ToRadians(pitch) / 2);
            pitchRotation.Y = 0;
            pitchRotation.Z = 0;
            pitchRotation.W = (float)Math.Cos(ToRadians(pitch) / 2);

            YawRotation.Multiply(pitchRotation);

            YawResult = (YawRotation * new Quaternion(0, 0, 1, 0)) * Quaternion.Conjugate(YawRotation);

            Vector3 new_forwar = new Vector3(YawResult.X, YawResult.Y, YawResult.Z);
            return new_forwar;
        }

        public void Move(float x, float y, float z)
        {
            Position += Forward * z;
            Position += Right * x;
            Position += Up * y;
        }

        public void SetEuler(float p, float y, float r)
        {
            float cosY = (float)Math.Cos(y / 2.0f);
            float sinY = (float)Math.Sin(y / 2.0f);
            float cosP = (float)Math.Cos(p / 2.0f);
            float sinP = (float)Math.Sin(p / 2.0f);
            float cosR = (float)Math.Cos(r / 2.0f);
            float sinR = (float)Math.Sin(r / 2.0f);

            RotationQuaternion = new Quaternion(
                cosR * sinP * cosY + sinR * cosP * sinY,
                cosR * cosP * sinY - sinR * sinP * cosY,
                sinR * cosP * cosY - cosR * sinP * sinY,
                cosR * cosP * cosY + sinR * sinP * sinY);
        }

        public static float ToRadians(float val)
  	    {
	        return (float)(Math.PI / 180) * val;
	    }
    }
}
